#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stddef.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>


static int granularity = 0;

DWORD
getgranularity (void)
{
	if (!granularity) {
		SYSTEM_INFO si;
		GetSystemInfo (&si);
		granularity = si.dwAllocationGranularity;
		}
	return granularity;
}

__ptr_t
__mmap64 (__ptr_t addr, size_t len, int prot, int flags, int fd,
		__off64_t offset)
{
	__ptr_t map = (__ptr_t) NULL;
	caddr_t gran_addr = (caddr_t) addr;
	HANDLE handle = INVALID_HANDLE_VALUE;
	DWORD cfm_flags = 0, mvf_flags = 0, sysgran = getgranularity ();
	__off64_t gran_offset = offset, filelen = _filelengthi64(fd);
	int mmlen = len;
	
	switch (prot) {
		case PROT_READ | PROT_WRITE | PROT_EXEC:
		case PROT_WRITE | PROT_EXEC:
			cfm_flags = PAGE_EXECUTE_READWRITE;
			mvf_flags = FILE_MAP_ALL_ACCESS;
			break;
		case PROT_READ | PROT_WRITE:
			cfm_flags = PAGE_READWRITE;
			mvf_flags = FILE_MAP_ALL_ACCESS;
			break;
		case PROT_WRITE:
			cfm_flags = PAGE_READWRITE;
			mvf_flags = FILE_MAP_WRITE;
			break;
		case PROT_READ:
			cfm_flags = PAGE_READONLY;
			mvf_flags = FILE_MAP_READ;
			break;
		case PROT_NONE:
			cfm_flags = PAGE_NOACCESS;
			mvf_flags = FILE_MAP_READ;
			break;
		case PROT_EXEC:
			cfm_flags = PAGE_EXECUTE;
			mvf_flags = FILE_MAP_READ;
			break;
	}
	if (flags & MAP_PRIVATE) {
		//if (IsWin9x ())
		//	cfm_flags = PAGE_WRITECOPY;
		mvf_flags = FILE_MAP_COPY;
	}
	fprintf (stderr, "Addr before:   %p\n", gran_addr);
	fprintf (stderr, "Offset before: %#I64X\n", gran_offset);
	if (flags & MAP_FIXED) {
		gran_offset = offset;
		gran_addr = (caddr_t)addr;
	}
	else {
		gran_offset = offset & ~(sysgran - 1);
		gran_addr = (caddr_t) (((DWORD) gran_addr / sysgran) * sysgran);
	}
	fprintf (stderr, "Addr after:    %p\n", gran_addr);
	fprintf (stderr, "Offset after:  %#I64X\n", gran_offset);
	mmlen = (filelen < gran_offset + len ? filelen - gran_offset : len);

	handle = CreateFileMapping ((HANDLE) _get_osfhandle(fd), NULL, cfm_flags,
		0, mmlen, NULL);
	if (!handle) {
		//set_werrno;
		//WinErr ("CreateFileMapping");
		return (__ptr_t)MAP_FAILED;
	}
	map = (__ptr_t) MapViewOfFileEx (handle, mvf_flags, HIWORD(gran_offset),
		LOWORD(gran_offset), (SIZE_T) mmlen, (LPVOID) gran_addr);
	if (map == NULL && (flags & MAP_FIXED) ) {
		fprintf (stderr, "Starting address: %p\n", (LPVOID) gran_addr);
		//WinErr ("First try of MapViewOfFileEx failed");
		map = (__ptr_t) MapViewOfFileEx (handle, mvf_flags, HIWORD(gran_offset),
			LOWORD(gran_offset), (SIZE_T) mmlen, (LPVOID) NULL);
	}
	CloseHandle(handle);
	if (map == NULL) {
		//set_werrno;
		//WinErr ("MapViewOfFileEx");
		return (__ptr_t)MAP_FAILED;
	}
	return map;
}

__ptr_t mmap (__ptr_t addr, size_t len, int prot, int flags, int fd,
		__off64_t offset)
{
	return __mmap64(addr, len, prot, flags, fd, offset);
}

int
__munmap (__ptr_t addr, size_t len)
{
  if (!UnmapViewOfFile( (LPCVOID) addr))
    return(-1);
  return(0);
}

int
munmap (__ptr_t addr, size_t len)
{
	return __munmap (addr, len);
}